Storage of data in variables and arrays is temporary; all
such data is lost when a program terminates. Files are
used for permanent retention of large amounts of data.
Computers store files on secondary storage devices,
especially disk storage devices. In this chapter, we
explain how data files are created, updated, and
processed by C programs. We consider both sequential
access files and random access files.<br>
<spacer width=16 height=1><br>
</page>
</section>
<section type=Body name=Default title="23.2 The Data Hierarchy">
<page>
<font size=18 bold>23.2 The Data Hierarchy</font><hr>
Ultimately, all data items processed by a computer are
reduced to combinations of zeros and ones. This occurs
because it is simple and economical to build electronic
devices that can assume two stable states--one of the
states represents <b>0</b> and the other represents <b>1</b>. It is
remarkable that the impressive functions performed by
computers involve only the most fundamental
manipulations of <b>0</b>s and <b>1</b>s.<br>
<spacer width=16 height=1>The smallest data item in a computer can assume the
value <b>0</b> or the value <b>1</b>. Such a data item is called a <i>bit</i>
(short for "<i>b</i>inary dig<i>it</i>"--a digit that can assume one of <br>
</page>
<page>
two values). Computer circuitry performs various
simple bit manipulations such as determining the value
of a bit, setting the value of a bit, and reversing a bit
(from <b>1</b> to <b>0</b> or from <b>0</b> to <b>1</b>).<br>
<spacer width=16 height=1>It is cumbersome for programmers to work with data in
the low-level form of bits. Instead, programmers prefer
to work with data in the form of <i>decimal digits</i> (i.e., 0,
1, 2, 3, 4, 5, 6, 7, 8, and 9), <i>letters</i> (i.e., A through Z,
and a through z), and <i>special symbols</i> (i.e., $, @, %, &,
*, (, ), -, +, ", :, ?, /, and many others). Digits, letters,
and special symbols are referred to as <i>characters</i>. The
set of all characters that may be used to write programs
and represent data items on a particular computer is <br>
</page>
<page>
called that computer's <i>character set</i>. Since computers
can process only <b>1</b>s and <b>0</b>s, every character in a
computer's character set is represented as a pattern of
<b>1</b>s and <b>0</b>s (called a <i>byte</i>). Today, bytes are most
commonly composed of eight bits. Programmers create
programs and data items as characters; computers then
manipulate and process these characters as patterns of
bits.<br>
<spacer width=16 height=1>Just as characters are composed of bits, <i>fields</i> are
composed of characters. A field is a group of characters
that conveys meaning. For example, a field consisting
solely of uppercase and lowercase letters can be used to
represent a person's name.<br>
</page>
<page>
Data items processed by computers form a <i>data
hierarchy</i> in which data items become larger and more
complex in structure as we progress from bits, to
characters (bytes), to fields, and so on.<br>
<spacer width=16 height=1>A <i>record</i> (i.e., a <b>struct</b> in C) is composed of several
fields. In a payroll system, for example, a record for a
particular employee might consist of the following
fields:<br>
1. Social Security number <br>
2. Name<br>
3. Address <br>
4. Hourly salary rate <br>
5. Number of exemptions claimed<br>
</page>
<page>
6. Year-to-date earnings <br>
7. Amount of Federal taxes withheld, etc.<br>
Thus, a record is a group of related fields. In the
preceding example, each of the fields belongs to the
same employee. Of course, a particular company may
have many employees, and will have a payroll record
for each employee. A <i>file</i> is a group of related records.
A company's payroll file normally contains one record
for each employee. Thus, a payroll file for a small
company might contain only 22 records, whereas a
payroll file for a large company might contain 100,000
records. It is not unusual for an organization to have
hundreds or even thousands of files, with many <br>
</page>
<page>
containing millions or even billions of characters of
information. With the increasing popularity of laser
optical disks and multimedia technology, even trillion-
byte files will soon be common. <b><a href="^Illustration::c:s0p1">Fig.<img src="bckgrnds/icons/ill_ico.gif" align=sidebar> 23.1</a></b> illustrates the
data hierarchy.<br>
<spacer width=16 height=1>To facilitate the retrieval of specific records from a file,
at least one field in each record is chosen as a <i>record
key</i>. A record key identifies a record as belonging to a
particular person or entity. For example, in the payroll
record described in this section, the Social Security
number would normally be chosen as the record key.<br>
<spacer width=16 height=1>There are many ways of organizing records in a file.
The most popular type of organization is called a <br>
</page>
<page>
<i>sequential file</i> in which records are typically stored in
order by the record key field. In a payroll file, records
are usually placed in order by Social Security number.
The first employee record in the file contains the lowest
Social Security number, and subsequent records contain
increasingly higher Social Security numbers. <br>
<spacer width=16 height=1>Most businesses utilize many different files to store
data. For example, companies may have payroll files,
accounts receivable files (listing money due from
clients), accounts payable files (listing money due to
suppliers), inventory files (listing facts about all the
items handled by the business), and many other types of
files. A group of related files is sometimes called a <br>
</page>
<page>
<i>database</i>. A collection of programs designed to create
and manage databases is called a <i>database management
system</i> (DBMS).<br>
</page>
</section>
<section type=Body name=Default title="23.3 Files and Streams">
<page>
<font size=18 bold>23.3 Files and Streams</font><hr>
C views each file simply as a sequential stream of bytes
(<a href="^Illustration::c:s0p2"><img src="bckgrnds/icons/ill_ico.gif" align=sidebar><b>Fig. 23.2</b></a>). Each file ends either with an <i>end-of-file
marker</i> or at a specific byte number recorded in a
system maintained, administrative data structure. When
a file is <i>opened</i>, a stream is associated with the file.
Three files and their associated streams are
automatically opened when program execution
begins--the <i>standard input</i>, the <i>standard output</i>, and
the <i>standard error</i>. Streams provide communication
channels between files and programs. For example, the
standard input stream enables a program to read data <br>
</page>
<page>
from the keyboard, and the standard output stream
enables a program to print data on the screen. Opening
a file returns a pointer to a <b>FILE</b> structure (defined in
<b><stdio.h></b>) that contains information used to process the
file. This structure includes a <i>file descriptor</i>, i.e., an
index into an operating system array called the <i>open file
table</i>. Each array element contains a <i>file control block
(FCB)</i> that the operating system uses to administer a
particular file. The standard input, standard output, and
standard error are manipulated using file pointers <b>stdin</b>,
<b>stdout</b>, and <b>stderr</b>.<br>
<spacer width=16 height=1>The standard library provides many functions for
reading data from files and for writing data to files. <br>
</page>
<page>
Function <b>fgetc</b>, like <b>getchar</b>, reads one character from a
file. Function <b>fgetc</b> receives as an argument a <b>FILE</b>
pointer for the file from which a character will be read.
The call <b>fgetc(stdin)</b> reads one character from <b>stdin</b>--
the standard input. This call is equivalent to the call
<b>getchar()</b>. Function <b>fputc</b>, like <b>putchar</b>, writes one
character to a file. Function <b>fputc</b> receives as
arguments a character to be written and a pointer for the
file to which the character will be written. The function
call <b>fputc('a',</b> <b>stdout)</b> writes the character <b>'a'</b> to
<b>stdout</b>--the standard output. This call is equivalent to
<b>putchar('a')</b>. <br>
</page>
<page>
Several other functions used to read data from standard
input and write data to standard output have similarly
named file processing functions. The <b>fgets</b> and <b>fputs</b>
functions, for example, can be used to read a line from a
file and write a line to a file, respectively. Their
counterparts for reading from standard input and
writing to standard output are <b>gets</b> and <b>puts</b>. In the next
several sections, we introduce the file processing
equivalents of functions <b>scanf</b> and <b>printf</b>--<b>fscanf</b> and
<b>fprintf</b>. Later in the chapter we discuss functions <b>fread</b>
and <b>fwrite</b>.<br>
<br>
</page>
</section>
<section type=Body name=Default title="23.4 Creating a Sequential Access File">
<page>
<font size=18 bold>23.4 Creating a Sequential Access File</font><hr>
C imposes no structure on a file. Thus, notions like a
record of a file do not exist as part of the C language.
Therefore, the programmer must provide any file
structure to meet the requirements of each particular
application. In the following example, we see how the
programmer may impose a record structure on a file.<br>
<spacer width=16 height=1>The program of <a href="^Code::c:s0p0"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.3 </b></a>creates a simple sequential
access file that might be used in an accounts receivable
system to help keep track of the amounts owed by a
company's credit clients. For each client, the program
obtains an account number, the client's name, and the <br>
</page>
<page>
client's balance (i.e., the amount the client owes the
company for goods and services received in the past).
The data obtained for each client constitutes a "record"
for that client. The account number is used as the record
key in this application--the file will be created and
maintained in account number order. This program
assumes the user enters the records in account number
order. In a comprehensive accounts receivable system, a
sorting capability would be provided so the user could
enter the records in any order. The records would then
be sorted and written to the file. <br>
<spacer width=16 height=1>Now let us examine this program. The statement<br>
<font size=2><br></font><font size=11><pre>
FILE *cfPtr;<p>
</pre></font>
</page>
<page>
states that <b>cfptr</b> is a pointer to a <b>FILE</b> structure. The C
program administers each file with a separate <b>FILE</b>
structure. The programmer need not know the specifics
of the <b>FILE</b> structure to use files. We will soon see
precisely how the <b>FILE</b> structure leads indirectly to the
operating system's file control block (FCB) for a file. <br>
<spacer width=16 height=1>Each open file must <a href="^Portable::c:s0p0"><img src="bckgrnds/icons/port_ico.gif" align=sidebar></a>have a separately declared pointer
of type <b>FILE</b> that is used to refer to the file. The line<br>
<font size=2><br></font><font size=11><pre>
if ((cfPtr = fopen("clients.dat", "w")) == NULL)<p>
</pre></font>
names the file--<b>"clients.dat"</b>--to be used by the
program and establishes a "line of communication"
with the file. The file pointer <b>cfPtr</b> is assigned a pointer
to the <b>FILE</b> structure for the file opened with <b>fopen</b>. <br>
</page>
<page>
Function <b>fopen</b> takes two arguments: a file name and a
<i>file open mode</i>. The file open mode <b>"w"</b> indicates that
the file is to be opened for <i>writing</i>. If a file does not
exist and it is opened for writing, <b>fopen</b> creates the file.
If an existing file is opened for writing, the contents of
the file are discarded without warning. In the program,
the <b>if</b> <a href="^Errors::c:s0p0"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>structure is used to determine whether the file
pointer <b>cfPtr</b> is <b>NULL</b> (i.e., the file is not opened). If it
is <b>NULL</b>, an <a href="^Errors::c:s0p1"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>error message is printed and the program
ends. Otherwise, the input is processed and written to
the file. <br>
<spacer width=16 height=1><b><spacer width=20 height=1></b>The program prompts the user to enter the various fields
for each record, or to enter end-of-file when data entry <br>
</page>
<page>
is complete. <a href="^Illustration::c:s0p3"><img src="bckgrnds/icons/ill_ico.gif" align=sidebar>Figure 23.4</a> lists the key combinations for
entering end-of-file for various computer systems. <br>
<spacer width=16 height=1>The line <br>
<font size=2><br></font><font size=11><pre>
while (!feof(stdin))<p>
</pre></font>
uses function <b>feof</b> to determine whether the <i>end-of-file
indicator</i> is set for the file to which <b>stdin</b> refers. The
end-of-file indicator informs the program that there is
no more data to be processed. In the program of <a href="^Code::c:s0p0">Fig.<img src="bckgrnds/icons/code_ico.gif" align=sidebar>
23.3</a>, the end-of-file indicator is set for the standard
input when the user enters the end-of-file key
combination. The argument to function <b>feof</b> is a pointer
to the file being tested for the end-of-file indicator
(<b>stdin</b> in this case). The function returns a nonzero <br>
</page>
<page>
value (true) once the end-of-file indicator has been set;
otherwise, zero is returned. The <b>while</b> structure that
includes the <b>feof</b> call in this program continues
executing while the end-of-file indicator is not set. <br>
writes <a href="^Errors::c:s0p2"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>data to the file <b>clients.dat</b>. The data may be
retrieved later by a program designed to read the file
(see <a href="#s5p0">Section 23.5</a>). Function <b>fprintf</b> is <a href="^Practice::c:s0p0"><img src="bckgrnds/icons/gpp_ico.gif" align=sidebar></a>equivalent to
<b>printf</b> except that <b>fprintf</b> also receives as an argument
a file pointer for the file to which the data will be
written. <br>
</page>
<page>
After the user enters end-of-file, the program closes the
<b>clients.dat</b> file with <b>fclose</b> and terminates. Function
<b>fclose</b> also receives the <a href="^Practice::c:s0p1"><img src="bckgrnds/icons/gpp_ico.gif" align=sidebar></a>file pointer (rather than the file
name) as an argument. If function <b>fclose</b> is not called
explicitly, the operating system normally will close the
file when program execution terminates. This is an
example of <a href="^Perform::c:s0p0"><img src="bckgrnds/icons/perf_ico.gif" align=sidebar></a>operating system "housekeeping."<br>
<spacer width=16 height=1>In the sample execution for the program of <a href="^Code::c:s0p0"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.3</b></a>, the
user enters information for five accounts, and then
enters end-of-file to signal that data entry is complete.
The sample execution does not show how the data
records actually appear in the file. To verify that the file
has been created successfully, in the next section we <br>
</page>
<page>
present a program that reads the file and prints its
contents. <br>
<spacer width=16 height=1><a href="^Illustration::c:s0p4"><img src="bckgrnds/icons/ill_ico.gif" align=sidebar><b>Figure 23.5</b></a> illustrates the relationship between <b>FILE</b>
pointers, <b>FILE</b> structures, and FCBs in memory. When
the file <b>"clients.dat"</b> is opened, an FCB for the file is
copied into memory. The figure shows the connection
between the file pointer returned by <b>fopen</b> and the FCB
used by the operating system to administer the file.<br>
<spacer width=16 height=1>Programs may process no files, one file, or several files.
Each file used in a program must have a unique name
and will have a different file pointer returned by <b>fopen</b>.
All subsequent file processing functions after the file is
opened must refer to the file with the appropriate file <br>
</page>
<page>
pointer. Files may be opened in one of several modes.
To <a href="^Errors::c:s0p3"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>create a file, or to discard the contents of a file
before <a href="^Errors::c:s0p4"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>writing data, open the file for writing (<b>"w"</b>). To
read an <a href="^Errors::c:s0p5"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>existing file, open it for reading (<b>"r"</b>). To add
records to the end of an existing <a href="^Errors::c:s0p6"><img src="bckgrnds/icons/cpe_ico.gif" align=sidebar></a>file, open the file for
appending (<b>"a"</b>). To open a file so that it may be
written to and read from, open the file for updating in
one of the three update modes--<b>"r+"</b>, <b>"w+"</b>, or <b>a+"</b>.
Mode <b>"r+"</b> opens a file for reading and writing. Mode
<b>"w+"</b> creates a file for reading and writing. If the file
already exists, the file is opened and the current
contents of the file are discarded. Mode <b>"a+"</b> opens a <br>
</page>
<page>
file for reading and writing--all writing is done at the
end of the file. If the file does not exist, it is created. <br>
<spacer width=16 height=1>If an <a href="^Practice::c:s0p2"><img src="bckgrnds/icons/gpp_ico.gif" align=sidebar></a>error occurs while opening a file in any mode,
<b>fopen</b> returns <b>NULL</b>.<br>
</page>
</section>
<section type=Body name=Default title="23.5 Reading Data from a Sequential Access File">
<page>
<font size=18 bold>23.5 Reading Data from a Sequential Access
File</font><hr>
Data are stored in files so that the data may be retrieved
for processing when needed. The previous section
demonstrated how to create a file for sequential access.
In this section, we discuss how to read data sequentially
from a file.<br>
<spacer width=16 height=1>The program of <a href="^Code::c:s0p1"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.7</b></a> reads records from the file
<b>"clients.dat"</b> created by the program of <a href="^Code::c:s0p0"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.3</b></a>, and
prints the contents of the records. The statement<br>
<font size=2><br></font><font size=11><pre>
FILE *cfPtr;<p>
</pre></font>
indicates that <b>cfPtr</b> is a pointer to a <b>FILE</b>. The line <br>
</page>
<page>
<font size=2><br></font><font size=11><pre>
if ((cfPtr = fopen("clients.dat", "r")) == NULL)<p>
</pre></font>
attempts to open the file <b>"clients.dat"</b> for reading
(<b>"r"</b>), and determines whether the file is opened
successfully (i.e., <b>fopen</b> does not return <b>NULL</b>). The
reads a "record" from the file. Function <b>fscanf</b> is
equivalent to function <b>scanf</b> except <b>fscanf</b> receives as
an argument a file pointer for the file from which the
data is read. After the preceding statement is executed
the first time, <b>account</b> will have the value <b>100</b>, <b>name</b>
will have the value <b>"Jones"</b>, and <b>balance</b> will have the <br>
</page>
<page>
value <b>24.98</b>. Each time the second <b>fscanf</b> statement is
executed, another record is read from the file and
<b>account</b>, <b>name</b>, and <b>balance</b> take on new values. When
the end of the file is reached, the file is closed and the
program terminates. <br>
<spacer width=16 height=1>To retrieve data sequentially from a file, a program
normally starts reading from the beginning of the file,
and reads all data consecutively until the desired data
are found. It may be desirable to process the data
sequentially in a file several times (from the beginning
of the file) during the execution of a program. A
statement such as<br>
<font size=2><br></font><font size=11><pre>
rewind(cfPtr);<p>
</pre></font>
</page>
<page>
causes a program's <i>file position pointer</i>--indicating the
number of the next byte in the file to be read or
written--to be repositioned to the beginning of the file
(i.e., byte 0) pointed to by <b>cfPtr</b>. The file position
pointer is not really a pointer. Rather it is an integer
value that specifies the byte location in the file at which
the next read or write is to occur. This is sometimes
referred to as the <i>file offset</i>. The file position pointer is a
member of the <b>FILE</b> structure associated with each file. <br>
<spacer width=16 height=1>We now present a program (<a href="^Code::c:s0p2"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.8</b></a>) that allows a credit
manager to obtain lists of customers with zero balances
(i.e., customers who do not owe any money), customers
with credit balances (i.e., customers to whom the <br>
</page>
<page>
company owes money), and customers with debit
balances (i.e., customers who owe the company money
for goods and services received). A credit balance is a
negative amount; a debit balance is a positive amount.<br>
<spacer width=16 height=1>The program displays a menu and allows the credit
manager to enter one of three options to obtain credit
information. Option 1 produces a list of accounts with
zero balances. Option 2 produces a list of accounts with
credit balances. Option 3 produces a list of accounts
with debit balances. Option 4 terminates program
execution. .<br>
<spacer width=16 height=1>Note that data in this type of sequential file cannot be
modified without the risk of destroying other data in the <br>
</page>
<page>
file. For example, if the name "<b>White</b>" needed to be
changed to "<b>Worthington</b>," the old name cannot
simply be overwritten. The record for <b>White</b> was
written to the file as<br>
<font size=2><br></font><font size=11><pre>
300 White 0.00<p>
</pre></font>
If the record is rewritten beginning at the same location
in the file using the new name, the record would be<p>
<br>
<font size=2><br></font><font size=11><pre>
300 Worthington 0.00<p>
</pre></font>
The new record is larger than the original record. The
characters beyond the second "<b>o</b>" in "<b>Worthington</b>"
would overwrite the beginning of the next sequential <br>
</page>
<page>
record in the file. The problem here is that in the
formatted input/output model using <b>fprintf</b> and <b>fscanf</b>,
fields--and hence records--can vary in size. For
example, 7, 14, -117, 2074, and 27383 are all <b>int</b>s
stored in the same number of bytes internally, but they
print on the screen or <b>fprintf</b> on the disk as different-
sized fields.<br>
<spacer width=16 height=1>Therefore, sequential access with <b>fprint</b> and <b>fscanf</b> is
not usually used to update records in place. Instead, the
entire file is usually rewritten. To make the preceding
name change, the records before <b>300</b> <b>White</b> <b>0.00</b> in
such a sequential access file would be copied to a new
file, the new record would be written, and the records <br>
</page>
<page>
after <b>300</b> <b>White</b> <b>0.00</b> would be copied to the new file.
This requires processing every record in the file to
update one record. <br>
</page>
</section>
<section type=Body name=Default title="23.6 Random Access Files">
<page>
<font size=18 bold>23.6 Random Access Files</font><hr>
As we stated previously, records in a file created with
the formatted output function <b>fprintf</b> are not
necessarily the same length. However, individual
records of a <i>randomly accessed file</i> are normally fixed
in length and may be accessed directly (and thus
quickly) without searching through other records. This
makes randomly accessed files appropriate for airline
systems, and other kinds of <i>transaction processing
systems</i> that require rapid access to specific data. There
are other ways of implementing randomly accessed <br>
</page>
<page>
files, but we will limit our discussion to this straight
forward approach using fixed-length records.<br>
<spacer width=16 height=1>Because every record in a randomly accessed file
normally has the same length, the exact location of a
record relative to the beginning of the file can be
calculated as a function of the record key. We will soon
see how this facilitates immediate access to specific
records, even in large files. <br>
<spacer width=16 height=1><a href="^Illustration::c:s0p7">Figure<img src="bckgrnds/icons/ill_ico.gif" align=sidebar> 23.10</a> illustrates one way to implement a
randomly accessed file. Such a file is like a freight train
with many cars--some empty and some with cargo.
Each car in the train is the same length.<br>
</page>
<page>
Data can be inserted in a randomly accessed file
without destroying other data in the file. Data stored
previously can also be updated or deleted without
rewriting the entire file. In the following sections we
explain how to create a randomly accessed file, enter
data, read the data both sequentially and randomly,
update the data, and delete data no longer needed.<br>
</page>
</section>
<section type=Body name=Default title="23.7 Creating a Randomly Accessed File">
<page>
<font size=18 bold>23.7 Creating a Randomly Accessed File</font><hr>
Function <b>fwrite</b> transfers a specified number of bytes
beginning at a specified location in memory to a file.
The data is written beginning at the location in the file
indicated by the file position pointer. Function <b>fread</b>
transfers a specified number of bytes from the location
in the file specified by the file position pointer to an
area in memory beginning with a specified address.
Now, when writing an integer, instead of using <br>
<font size=2><br></font><font size=11><pre>
fprintf(fPtr, "%d", number);<p>
</pre></font>
</page>
<page>
which could print as few as 1 digit or as many as 11
digits (10 digits plus a sign, each of which requires 1
byte of storage) for a 4-byte integer, we can use<br>
<font size=2><br></font><font size=11><pre>
fwrite(&number, sizeof(int), 1, fPtr);<p>
</pre></font>
which always writes 4 bytes (or 2 bytes on a system
with 2-byte integers) from variable <b>number</b> to the file
represented by <b>fPtr</b> (we will explain the <b>1</b> argument
shortly). Later, <b>fread</b> can be used to read 4 of those
bytes into integer variable <b>number</b>. Although <b>fread</b>
and <b>fwrite</b> read and write data such as integers in fixed-
size rather than variable-size format, the data they
handle is processed in computer "raw data" format (i.e., <br>
</page>
<page>
bytes of data) rather than in <b>printf</b>'s and <b>scanf</b>'s
human-readable format. <br>
<spacer width=16 height=1>The <b>fwrite</b> and <b>fread</b> functions are capable of reading
and writing arrays of data to and from disk. The third
argument of both <b>fread</b> and <b>fwrite</b> is the number of
elements in the array that should be read from disk or
written to disk. The preceding <b>fwrite</b> function call
writes a single integer to disk, so the third argument is <b>1</b>
(as if one element of an array is being written).<br>
<spacer width=16 height=1>File processing programs rarely write a single field to a
file. Normally, they write one <b>struct</b> at a time, as we
show in the following examples. <br>
<spacer width=16 height=1>Consider the following problem statement:<br>
</page>
<page>
<i>Create a credit processing system capable of storing up
to 100 fixed-length records. Each record should consist
of an account number that will be used as the record
key, a last name, a first name, and a balance. The
resulting program should be able to update an account,
insert a new account record, delete an account, and list
all the account records in a formatted text file for
printing. Use a randomly accessed file.
</i><br>
<spacer width=16 height=1>The next several sections introduce the techniques
necessary to create the credit processing program. The
program of <a href="^Code::c:s0p3"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.11</b></a> shows how to open a randomly
accessed file, define a record format using a <b>struct</b>,
write data to the disk, and close the file. This program <br>
</page>
<page>
initializes all 100 records of the file <b>"credit.dat"</b> with
empty <b>struct</b>s using function <b>fwrite</b>. Each empty <b>struct</b>
contains <b>0</b> for the account number, <b>NULL</b> (represented
by empty quotation marks) for the last name, <b>NULL</b> for
the first name, and <b>0.0</b> for the balance. The file is
initialized in this manner to create space on the disk in
which the file will be stored, and to make it possible to
determine if a record contains data.<br>
<spacer width=16 height=1>Function <b>fwrite</b> writes a block (specific number of
bytes) of data to a file. In our program, the statement<br>
causes the structure <b>blankClient</b> of size <b>sizeof(struct</b>
<b>clientData)</b> to be written to the file pointed to by <b>cfPtr</b>.
The operator <b>sizeof</b> returns the size in bytes of the
object contained in <a href="^Perform::c:s0p1"><img src="bckgrnds/icons/perf_ico.gif" align=sidebar></a>parentheses (in this case <b>struct</b>
<b>clientData</b>). The <b>sizeof</b> operator is a compile-time
unary operator that returns an unsigned integer. The
<b>sizeof</b> operator can be used to determine the size in
bytes of any data type or expression. For example,
<b>sizeof(int)</b> is used to determine whether an integer is
stored in 2 or 4 bytes on a particular computer. <br>
<spacer width=16 height=1>Function <b>fwrite</b> can actually be used to write several
elements of an array of objects. To write several array
elements, the programmer supplies a pointer to an array <br>
</page>
<page>
as the first argument in the call to <b>fwrite</b>, and specifies
the number of elements to be written as the third
argument in the call to <b>fwrite</b>. In the preceding
statement, <b>fwrite</b> was used to write a single object that
was not an array element. Writing a single object is
equivalent to writing one element of an array, hence the
<b>1</b> in the <b>fwrite</b> call.<br>
</page>
</section>
<section type=Body name=Default title="23.8 Writing Data Randomly to a Randomly Accessed File">
<page>
<font size=18 bold>23.8 Writing Data Randomly to a Randomly
Accessed File</font><hr>
The program of <a href="^Code::c:s0p4"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.12</b></a> writes data to the file
<b>"credit.dat"</b>. It uses the combination of <b>fseek</b> and
<b>fwrite</b> to store data at specific locations in the file.
Function <b>fseek</b> sets the file position pointer to a specific
position in the file, then <b>fwrite</b> writes the data. <br>
<spacer width=16 height=1>The statement<br>
<font size=2><br></font><font size=11><pre>
fseek(cfPtr, (accountNum - 1) <p>
* sizeof(struct clientData), SEEK_SET);<p>
</pre></font>
positions the file position pointer for the file referenced
by <b>cfPtr</b> to the byte location calculated by <br>
</page>
<page>
<b>(accountNum</b> <b>-</b> <b>1)</b> <b>*</b> <b>sizeof(struct</b> <b>clientData)</b>; the
value of this expression is called the <i>offset</i> or the
<i>displacement</i>. Because the account number is between
1 and 100 but the byte positions in the file start with <b>0</b>, 1
is subtracted from the account number when calculating
the byte location of the record. Thus, for record 1, the
file position pointer is set to byte 0 of the file. The
symbolic constant <b>SEEK_SET</b> indicates that the file
position pointer is positioned relative to the beginning
of the file by the amount of the offset. As the above
statement indicates, a seek for account number 1 in the
file sets the file position pointer to the beginning of the
file because the byte location calculated is 0. <a href="^Illustration::c:s0p8"><img src="bckgrnds/icons/ill_ico.gif" align=sidebar><b>Figure 23.14</b></a> <br>
</page>
<page>
illustrates the file pointer referring to a <b>FILE</b> structure
in memory. The file position pointer indicates that the
next byte to be read or written is 5 bytes from the
beginning of the file.<br>
<spacer width=16 height=1>The ANSI standard shows the function prototype for
<b>fseek</b> as<br>
<font size=2><br></font><font size=11><pre>
int fseek(FILE *stream, long int offset, int whence);<p>
</pre></font>
where <b>offset</b> is the number of bytes from location
<b>whence</b> in the file pointed to by <b>stream</b>. The argument
<b>whence</b> can have one of three values--<b>SEEK_SET</b>,
<b>SEEK_CUR</b> or <b>SEEK_END</b>--indicating the location
in the file from which the seek begins. <b>SEEK_SET</b> <br>
</page>
<page>
indicates that the seek starts at the beginning of the file;
<b>SEEK_CUR</b> indicates that the seek starts at the current
location in the file; and <b>SEEK_END</b> indicates that the
seek starts at the end of the file. These three symbolic
constants are defined in the <b>stdio.h</b> header file.<br>
</page>
</section>
<section type=Body name=Default title="23.9 Reading Data Randomly from a Randomly Accessed File">
<page>
<font size=18 bold>23.9 Reading Data Randomly from a Randomly Accessed File</font><hr>
Function <b>fread</b> reads a specified number of bytes from
a file into memory. For example, the statement<br>
reads the number of bytes determined by <b>sizeof(struct</b>
<b>clientData)</b> from the file referenced by <b>cfPtr</b> and stores
the data in the structure <b>client</b>. The bytes are read from
the location in the file specified by the file position
pointer. Function <b>fread</b> can be used to read several
fixed-size array elements by providing a pointer to the <br>
</page>
<page>
array in which the elements will be stored, and by
indicating the number of elements to be read. The
preceding statement specifies that one element should
be read. To read more than one element, specify the
number of elements in the third argument of the <b>fread</b>
statement. <br>
<spacer width=16 height=1>The program of <a href="^Code::c:s0p5"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.15</b></a> reads sequentially every record
in the <b>"credit.dat"</b> file, determines whether each
record contains data, and prints the formatted data for
records containing data. The <b>feof</b> function determines
when the end of the file is reached, and the <b>fread</b>
function transfers data from the disk to the <b>clientData</b>
structure <b>client</b>. <br>
</page>
</section>
<section type=Body name=Default title="23.10 Case Study: A Transaction Processing Program">
<page>
<font size=18 bold>23.10 Case Study: A Transaction<p>
Processing Program</font><hr>
We now present a substantial transaction processing
program using randomly accessed files. The program
maintains a bank's account information. The program
updates existing accounts, adds new accounts, deletes
accounts, and stores a listing of all the current accounts
in a text file for printing. We assume that the program
of <a href="^Code::c:s0p3"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.11</b></a> has been executed to create the file
<b>credit.dat</b>.<br>
<spacer width=16 height=1>The program has five options. Option 1 calls function
<b>textFile</b> to store a formatted list of all the accounts in a <br>
</page>
<page>
text file called <b>accounts.txt</b> that may be printed later.
The function uses <b>fread</b> and the sequential file access
techniques used in the program of Fig. 11.15. After
choosing option 1 the file <b>accounts.txt</b> contains:<br>
<font size=2><br></font><font size=11><pre>
Acct Last Name First Name Balance<p>
29 Brown Nancy -24.54<p>
33 Dunn Stacey 314.33<p>
37 Barker Doug 0.00<p>
88 Smith Dave 258.34<p>
96 Stone Sam 34.98<p>
<p>
</pre></font>
Option 2 calls the function <b>updateRecord</b> to update an
account. The function will only update a record that
already exists, so the function first checks to see if the <br>
</page>
<page>
record specified by the user is empty. The record is read
into structure <b>client</b> with <b>fread</b>, then member <b>acctNum</b>
is compared to 0. If it is 0, the record contains no
information, and a message is printed stating that the
record is empty. Then, the menu choices are displayed.
If the record contains information, function
<b>updateRecord</b> inputs the transaction amount,
calculates the new balance, and rewrites the record to
the file. A typical output for option 2 is:<br>
<font size=2><br></font><font size=11><pre>
<p>
Enter account to update (1 - 100): 37<p>
37 Barker Doug 0.00<p>
<p>
Enter charge (+) or payment (-): +87.99<p><p>
</pre></font>
</page>
<page>
<font size=2><br></font><font size=11><pre>
37 Barker Doug 87.99<p>
<p>
</pre></font>
Option 3 calls the function <b>newRecord</b> to add a new
account to the file. If the user enters an account number
for an existing account, <b>newRecord</b> displays an error
message that the record already contains information,
and the menu choices are printed again. This function
uses the same process to add a new account as does the
program in <a href="^Code::c:s0p4"><img src="bckgrnds/icons/code_ico.gif" align=sidebar><b>Fig. 23.12</b></a>. A typical output for option 3 is<br>
<font size=2><br></font><font size=11><pre>
<p>
Enter new account number (1 - 100): 22<p>
Enter lastname, firstname, balance<p>
? Johnston Sarah 247.45<p>
<p>
</pre></font>
</page>
<page>
Option 4 calls function <b>deleteRecord</b> to delete a record
from the file. Deletion is accomplished by asking the
user for the account number and reinitializing the
record. If the account contains no information,
<b>deleteRecord</b> displays an error message that the
account does not exist. Option 5 terminates program
execution. The program is shown in <b><a href="^Code::c:s0p6">F<img src="bckgrnds/icons/code_ico.gif" align=sidebar>ig. 23.16</a></b>. Note that
the file <b>"credit.dat"</b> is opened for update (reading and
<indent width=8 delay>* All data items processed by a computer are reduced
to combinations of zeros and ones. </indent>
<indent width=8 delay>* The smallest data item in a computer can assume the
value <b>0</b> or the value <b>1</b>. Such a data item is called a bit
(short for "binary digit"--a digit that can assume one of
two values). </indent>
<indent width=8 delay>* Digits, letters, and special symbols are referred to as
characters. The set of all characters that may be used to
write programs and represent data items on a particular
computer is called that computer's character set. Every
character in the computer's character set is represented </indent>
</page>
<page>
<indent width=8 delay>* as a pattern of eight <b>1</b>s and <b>0</b>s (called a byte). </indent>
<indent width=8 delay>* A field is a group of characters that conveys meaning. </indent>
<indent width=8 delay>* A record is a group of related fields. </indent>
<indent width=8 delay>* At least one field in each record is normally chosen
as a record key. The record key identifies a record as
belonging to a particular person or entity. </indent>
<indent width=8 delay>* The most popular type of organization for records in
a file is called a sequential access file in which records
are accessed consecutively until the desired data are
located. </indent>
<indent width=8 delay>* A group of related files is sometimes called a database. A collection of programs designed to create and </indent>
</page>
<page>
<indent width=8 delay>* manage databases is called a database management system (DBMS).</indent>
<indent width=8 delay>* C views each file simply as a sequential stream of
bytes.</indent>
<indent width=8 delay>* C automatically opens three files and their associated
streams--standard input, standard output, and standard
error--when program execution begins.</indent>
<indent width=8 delay>* The file pointers assigned to the standard input, standard output, and standard error are <b>stdin</b>, <b>stdout</b>, and
<b>stderr</b>, respectively.</indent>
<indent width=8 delay>* Function <b>fgetc</b> reads a character from a specified file. </indent>
<indent width=8 delay>* Function <b>fputc</b> writes a character to a specified file. </indent>
<indent width=8 delay>* Function <b>fgets</b> reads a line from a specified file.</indent>
</page>
<page>
<indent width=8 delay>* Function <b>fputs</b> writes a line to a specified file.</indent>
<indent width=8 delay>* <b>FILE</b> is a structure type defined in the <b>stdio.h</b> header
file. The programmer need not know the specifics of
this structure to use files. As a file is opened, a pointer
to the file's <b>FILE</b> structure is returned. </indent>
<indent width=8 delay>* Function <b>fopen</b> takes two arguments--a file name
and a file open mode--and opens the file. If the file
exists, the contents of the file are discarded without
warning. If the file does not exist and the file is being
opened for writing, <b>fopen</b> creates the file. </indent>
<indent width=8 delay>* Function <b>feof</b> determines whether the end-of-file
indicator for a file has been set. </indent>
<indent width=8 delay>* Function <b>fprintf</b> is equivalent to <b>printf</b> except </indent>
</page>
<page>
<indent width=8 delay>* <b>fprintf</b> receives as an argument a pointer to the file to
which the data will be written. </indent>
<indent width=8 delay>* Function <b>fclose</b> closes the file pointed to by its argument.</indent>
<indent width=8 delay>* To create a file, or to discard the contents of a file
before writing data, open the file for writing (<b>"w"</b>). To
read an existing file, open it for reading (<b>"r"</b>). To add
records to the end of an existing file, open the file for
appending (<b>"a"</b>). To open a file so that it may be written to and read from, open the file for updating in one of
the three updating modes--<b>"r+"</b>, <b>"w+"</b>, or <b>"a+"</b>.
Mode <b>"r+"</b> simply opens the file for reading and writing. Mode <b>"w+"</b> creates the file if it does not exist, and </indent>
</page>
<page>
<indent width=8 delay>* discards the current contents of the file if it does exist.
Mode <b>"a+"</b> creates the file if it does not exist, and writing is done at the end of the file. </indent>
<indent width=8 delay>* Function <b>fscanf</b> is equivalent to <b>scanf</b> except <b>fscanf</b>
receives as an argument a pointer to the file (normally
other than <b>stdin</b>) from which the data will be read. </indent>
<indent width=8 delay>* Function <b>rewind</b> causes the program to reposition
the file position pointer for the specified file to the
beginning of the file.</indent>
<indent width=8 delay>* Random access file processing is used to access a
record directly. </indent>
<indent width=8 delay>* To facilitate random access, data is stored in fixed-
length records. Since every record is the same length, </indent>
</page>
<page>
<indent width=8 delay>* the computer can quickly calculate (as a function of the
record key) the exact location of a record in relation to
the beginning of the file.</indent>
<indent width=8 delay>* Data can be added easily to a random access file
without destroying other data in the file. Data stored
previously in a file with fixed-length records can also
be changed and deleted without rewriting the entire file.</indent>
<indent width=8 delay>* Function <b>fwrite</b> writes a block (specific number of
bytes) of data to a file. </indent>
<indent width=8 delay>* The compile-time operator <b>sizeof</b> returns the size in
bytes of its operand.</indent>
<indent width=8 delay>* Function <b>fseek</b> sets the file position pointer to a specific position in a file based on the starting location of </indent>
</page>
<page>
<indent width=8 delay>* the seek in the file. The seek can start from one of three
locations--<b>SEEK_SET</b> starts from the beginning of
the file, <b>SEEK_CUR</b> starts from the current position in
the file, and <b>SEEK_END</b> starts from the end of the file.</indent>
<indent width=8 delay>* Function <b>fread</b> reads a block (specific number of
bytes) of data from a file. </indent>
<br>
</page>
</section>
<section type=Popup name=Debug title="Testing">
<page>
This chapter does not contain any Testing and Debugging tips.